import yaml
import os
from typing import Dict, Any, Optional
from pathlib import Path


class BaseConfigLoader:
    """支持子目录的配置加载器：基于文件名（含相对路径）的单例模式"""
    _instances: Dict[str, 'BaseConfigLoader'] = {}  # 用文件名（含路径）作为键
    _project_root = None  # 全局缓存项目根目录

    def __new__(cls, config_filename: str = None, root_marker: str = "poetry.toml"):
        key = config_filename  # 键包含相对路径（如"task/rf_config.yaml"）
        if key not in cls._instances:
            cls._instances[key] = super().__new__(cls)
        return cls._instances[key]

    def __init__(self, config_filename: str = None, root_marker: str = "poetry.toml"):
        if hasattr(self, '_initialized') and self._initialized:
            return

        self.root_marker = root_marker
        self.project_root = self._find_project_root()

        # 配置根目录（固定为项目下的config目录）
        self.config_root = self.project_root.joinpath("config")
        if not self.config_root.exists():
            raise FileNotFoundError(f"配置根目录不存在：{self.config_root}")

        # 处理配置文件路径（支持子目录，如"task/rf_config.yaml"）
        if not config_filename:
            self.config_path = self.config_root.joinpath("base_config.yaml")
        else:
            # 拼接路径时保留子目录结构
            self.config_path = self.config_root.joinpath(config_filename)

        # 检查文件是否存在（增加详细错误信息）
        if not self.config_path.exists():
            raise FileNotFoundError(
                f"配置文件不存在：{self.config_path}\n"
                f"请确认文件路径是否正确，当前查找的完整路径如上"
            )

        # 加载配置
        self.config = self._load_config()
        self._process_paths()

        self.tsv_sep = "\t"
        self.csv_sep = ","
        self._initialized = True

    def _find_project_root(self) -> Path:
        if BaseConfigLoader._project_root:
            return BaseConfigLoader._project_root
        current_dir = Path.cwd()
        while True:
            if (current_dir.joinpath(self.root_marker)).exists():
                BaseConfigLoader._project_root = current_dir
                return current_dir
            if current_dir.parent == current_dir:
                raise RuntimeError(f"未找到项目根目录（缺失标志文件：{self.root_marker}）")
            current_dir = current_dir.parent

    def _load_config(self) -> Dict[str, Any]:
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                config = yaml.safe_load(f) or {}
            if '_base_' in config:
                # 基础配置也支持子目录路径
                base_loader = BaseConfigLoader(config_filename=config['_base_'])
                base_config = base_loader.get_all()
                base_config.update(config)
                del base_config['_base_']
                return base_config
            return config
        except Exception as e:
            raise RuntimeError(f"加载配置失败：{self.config_path}，错误：{str(e)}")

    def _process_paths(self) -> None:
        def _recursive_process(node: Any) -> Any:
            if isinstance(node, dict):
                return {k: _recursive_process(v) for k, v in node.items()}
            if (isinstance(node, str)
                    and not Path(node).is_absolute()
                    and (os.sep in node or '/' in node)):
                return self.project_root.joinpath(node)
            return node

        self.config = _recursive_process(self.config)

    def get(self, key: str, default: Optional[Any] = None) -> Any:
        current = self.config
        for k in key.split('.'):
            if isinstance(current, dict) and k in current:
                current = current[k]
            else:
                return default
        return current

    def set(self, key: str, value: Any) -> None:
        current = self.config
        keys = key.split('.')
        for k in keys[:-1]:
            current = current.setdefault(k, {})
        # if isinstance(value, str) and not Path(value).is_absolute() and (os.sep in value or '/' in value):
        #     value = str((self.project_root.joinpath(value)).resolve())
        current[keys[-1]] = value

    def get_all(self) -> Dict[str, Any]:
        return self.config

    def get_root_path(self) -> Path:
        return self.project_root


# 测试代码（注意传入子目录路径）
if __name__ == '__main__':
    try:
        print(BaseConfigLoader() is BaseConfigLoader())
        # 加载位于config/task/下的rf_config.yaml
        config_rf = BaseConfigLoader("task/rf_config.yaml")

        # 验证单例特性
        config_rf2 = BaseConfigLoader("task/rf_config.yaml")
        print(f"rf配置是否为同一实例：{config_rf is config_rf2}")  # 应输出True

        print(config_rf.get("system.resources.src_data.train_path"))

    except Exception as e:
        print(f"错误：{e}")
